/** @file

  Copyright (c) 2004  - 2019, Intel Corporation. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

Module Name:


    PciPlatform.c

Abstract:
--*/


#include "PciPlatform.h"
#include "PchRegs.h"
#include "PchAccess.h"
#include "VlvCommonDefinitions.h"
#include "PlatformBootMode.h"

#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Protocol/PciIo.h>
#include <Guid/SetupVariable.h>
#include <Protocol/PciRootBridgeIo.h>
#include "SetupMode.h"
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Protocol/FirmwareVolume2.h>
#include <Library/HobLib.h>
#include <IndustryStandard/Pci22.h>

extern  PCI_OPTION_ROM_TABLE  mPciOptionRomTable[];
extern  UINTN                 mSizeOptionRomTable;

EFI_PCI_PLATFORM_PROTOCOL mPciPlatform = {
  PhaseNotify,
  PlatformPrepController,
  GetPlatformPolicy,
  GetPciRom
};

EFI_HANDLE mPciPlatformHandle = NULL;


SYSTEM_CONFIGURATION          mSystemConfiguration;

EFI_STATUS
GetRawImage (
  IN EFI_GUID   *NameGuid,
  IN OUT VOID   **Buffer,
  IN OUT UINTN  *Size
  )
{
  EFI_STATUS                    Status;
  EFI_HANDLE                    *HandleBuffer;
  UINTN                         HandleCount;
  UINTN                         Index;
  EFI_FIRMWARE_VOLUME2_PROTOCOL  *Fv;
  UINT32                        AuthenticationStatus;

  Status = gBS->LocateHandleBuffer (
                  ByProtocol,
                  &gEfiFirmwareVolume2ProtocolGuid,
                  NULL,
                  &HandleCount,
                  &HandleBuffer
                  );
  if (EFI_ERROR (Status) || HandleCount == 0) {
    return EFI_NOT_FOUND;
  }

  //
  // Find desired image in all Fvs
  //
  for (Index = 0; Index < HandleCount; Index++) {
    Status = gBS->HandleProtocol(
                    HandleBuffer[Index],
                    &gEfiFirmwareVolume2ProtocolGuid,
                    (VOID **) &Fv
                    );

    if ( EFI_ERROR ( Status ) ) {
      return EFI_LOAD_ERROR;
    }

    //
    // Try a raw file
    //
    *Buffer = NULL;
    *Size = 0;
    Status = Fv->ReadSection (
                   Fv,
                   NameGuid,
                   EFI_SECTION_RAW,
                   0,
                   Buffer,
                   Size,
                   &AuthenticationStatus
                   );

    if ( !EFI_ERROR ( Status )) {
        break;
    }
  }

  if ( Index >= HandleCount ) {
    return EFI_NOT_FOUND;
  }

  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI    
PhaseNotify (
  IN EFI_PCI_PLATFORM_PROTOCOL              *This,
  IN  EFI_HANDLE                                     HostBridge,
  IN  EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PHASE  Phase,
  IN  EFI_PCI_CHIPSET_EXECUTION_PHASE                ChipsetPhase
  )
{
  return EFI_UNSUPPORTED;
}


EFI_STATUS
EFIAPI
PlatformPrepController (
  IN EFI_PCI_PLATFORM_PROTOCOL                      *This,
  IN  EFI_HANDLE                                     HostBridge,
  IN  EFI_HANDLE                                     RootBridge,
  IN  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS    PciAddress,
  IN  EFI_PCI_CONTROLLER_RESOURCE_ALLOCATION_PHASE   Phase,
  IN  EFI_PCI_CHIPSET_EXECUTION_PHASE                ChipsetPhase
  )
{
  return EFI_UNSUPPORTED;
}

EFI_STATUS
EFIAPI
GetPlatformPolicy (
  IN CONST EFI_PCI_PLATFORM_PROTOCOL        *This,
  OUT EFI_PCI_PLATFORM_POLICY               *PciPolicy
  )
{
  *PciPolicy = EFI_RESERVE_VGA_IO_ALIAS;
  return EFI_SUCCESS;
}

/**
  GetPciRom from platform specific location for specific PCI device

  @param This        Protocol instance
  @param PciHandle   Identify the specific PCI devic
  @param RomImage    Returns the ROM Image memory location
  @param RomSize     Returns Rom Image size

  @retval EFI_SUCCESS
  @retval EFI_NOT_FOUND
  @retval  EFI_OUT_OF_RESOURCES

**/
EFI_STATUS
EFIAPI    
GetPciRom (
  IN CONST EFI_PCI_PLATFORM_PROTOCOL     *This,
  IN EFI_HANDLE                           PciHandle,
  OUT  VOID                               **RomImage,
  OUT  UINTN                              *RomSize
  )
{
  EFI_STATUS                    Status;
  EFI_PCI_IO_PROTOCOL           *PciIo;
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
  UINTN                         Segment;
  UINTN                         Bus;
  UINTN                         Device;
  UINTN                         Function;
  UINT16                        VendorId;
  UINT16                        DeviceId;
  UINT16                        DeviceClass;
  UINTN                         TableIndex;
  UINT8                         Data8;
  BOOLEAN                       MfgMode;
  EFI_PLATFORM_SETUP_ID         *BootModeBuffer;

  EFI_PEI_HOB_POINTERS        GuidHob;

  MfgMode = FALSE;

//
// Check if system is in manufacturing mode.
//
  GuidHob.Raw = GetHobList ();
  if (GuidHob.Raw == NULL) {
    return EFI_NOT_FOUND;
  }

  if ((GuidHob.Raw = GetNextGuidHob (&gEfiPlatformBootModeGuid, GuidHob.Raw)) != NULL) {
    BootModeBuffer = GET_GUID_HOB_DATA (GuidHob.Guid);
    if (!CompareMem (&BootModeBuffer->SetupName, MANUFACTURE_SETUP_NAME,
        StrSize (MANUFACTURE_SETUP_NAME)))
      {
      	//
        // System is in manufacturing mode.
        //
        MfgMode = TRUE;
      }
   }

  Status = gBS->HandleProtocol (
                  PciHandle,
                  &gEfiPciIoProtocolGuid,
                  (void **)&PciIo
                  );
  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }

  Status = gBS->LocateProtocol (
                  &gEfiPciRootBridgeIoProtocolGuid,
                  NULL,
                  (void **)&PciRootBridgeIo
                  );

  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }

  PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, 0x0A, 1, &DeviceClass);

  PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);

  PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, 0, 1, &VendorId);

  PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, 2, 1, &DeviceId);

  //
  // WA for PCIe SATA card (SYBA SY-PEX400-40)
  //
  if ((VendorId == 0x1B21) && (DeviceId == 0x0612)) {
    Data8 = 0x07;
    PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 4, 1, &Data8);
  }

    //
    // Do not run RAID or AHCI Option ROM if IDE
    //
    if (DeviceClass == ((PCI_CLASS_MASS_STORAGE << 8 ) | PCI_CLASS_MASS_STORAGE_IDE)) {
      return EFI_NOT_FOUND;
    }

    //
    // Run PXE ROM only if Boot network is enabled and not in MFG mode
    //
    if (DeviceClass == ((PCI_CLASS_NETWORK << 8 ) | PCI_CLASS_NETWORK_ETHERNET)) {
      if (((mSystemConfiguration.BootNetwork == 0) && (MfgMode == FALSE )) || (mSystemConfiguration.FastBoot == 1)) {
      return EFI_NOT_FOUND;
      }
    }

    //
    // Loop through table of Onboard option rom descriptions
    //
    for (TableIndex = 0; mPciOptionRomTable[TableIndex].VendorId != 0xffff; TableIndex++) {

      //
      // See if the PCI device specified by PciHandle matches at device in mPciOptionRomTable
      //
      if (VendorId != mPciOptionRomTable[TableIndex].VendorId ||
          DeviceId != mPciOptionRomTable[TableIndex].DeviceId ||
          ((DeviceClass == ((PCI_CLASS_NETWORK << 8 ) | PCI_CLASS_NETWORK_ETHERNET)) &&
           (mPciOptionRomTable[TableIndex].Flag != mSystemConfiguration.BootNetwork))  ) {
        continue;
      }

      Status = GetRawImage(
                 &mPciOptionRomTable[TableIndex].FileName,
                 RomImage,
                 RomSize
                 );

      if ((VendorId == IGD_VID) && (DeviceId == IGD_DID_VLV_A0)) {
        *(UINT16 *)(((UINTN) *RomImage) + OPROM_DID_OFFSET) = IGD_DID_VLV_A0;
      }

      if ((VendorId == IGD_VID) && (DeviceId == IGD_DID_II)) {
        *(UINT16 *)(((UINTN) *RomImage) + OPROM_DID_OFFSET) = IGD_DID_II;
      }

      if ((VendorId == IGD_VID) && (DeviceId == IGD_DID_0BE4)) {
        *(UINT16 *)(((UINTN) *RomImage) + OPROM_DID_OFFSET) = IGD_DID_0BE4;
      }

      if ((VendorId == IGD_VID) && (DeviceId == IGD_DID_QS)) {
        *(UINT16 *)(((UINTN) *RomImage) + OPROM_DID_OFFSET) = IGD_DID_QS;
      }


        if (EFI_ERROR (Status)) {
          continue;
        }
        return EFI_SUCCESS;
      }

  return EFI_NOT_FOUND;
}

/**

  @param  (Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT)

  @retval EFI_STATUS

**/
EFI_STATUS
EFIAPI
PciPlatformDriverEntry (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  UINTN       VarSize;

  VarSize = sizeof(SYSTEM_CONFIGURATION);
  Status = gRT->GetVariable(
                  L"Setup",
                  &gEfiNormalSetupGuid,
                  NULL,
                  &VarSize,
                  &mSystemConfiguration
                  );
  if (EFI_ERROR (Status) || VarSize != sizeof(SYSTEM_CONFIGURATION)) {
    //The setup variable is corrupted
    VarSize = sizeof(SYSTEM_CONFIGURATION);
    Status = gRT->GetVariable(
              L"SetupRecovery",
              &gEfiNormalSetupGuid,
              NULL,
              &VarSize,
              &mSystemConfiguration
              );
    ASSERT_EFI_ERROR (Status);
  }  

  //
  // Install on a new handle
  //
  Status = gBS->InstallProtocolInterface (
                  &mPciPlatformHandle,
                  &gEfiPciPlatformProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &mPciPlatform
                  );

  return Status;
}


